shhh <- suppressPackageStartupMessages # It's a library, so shhh!

shhh(library( mgcv ))
shhh(library(dplyr))
shhh(library(ggplot2))
shhh(library(lme4))
shhh(library(tidymv))
shhh(library(gamlss))
shhh(library(gsubfn))
shhh(library(lmerTest))
shhh(library(tidyverse))
shhh(library(boot))
shhh(library(rsample))
shhh(library(plotrix))
shhh(library(ggrepel))
shhh(library(mgcv))

theme_set(theme_bw())
options(digits=4)
options(scipen=999)
set.seed(444)
pipe_message = function(.data, status) {message(status); .data}

Read in MoTR Data


rate = 160

file_prefix = "/Users/cui/Desktop/motr_provo_160/"
fnames = list.files(path=file_prefix)

df = data.frame()
for (f in fnames) {
  temp = read.csv(paste0(file_prefix, "/", f)) %>%
    mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
    rename(go_past_time = go_pass_time)
  df = rbind(df, temp)
}

# View(df)

filter_df = df %>%
  group_by(para_nr, subj) %>%
    summarise(correct = if_else(unique(correctness) == 1, 1, 0)) %>%
  ungroup() %>%
  drop_na() %>%
  group_by(subj) %>%
    summarise(p_correct = mean(correct)) %>%
  ungroup() %>%
  mutate(p_correct = round(p_correct, digits = 2))
`summarise()` has grouped output by 'para_nr'. You can override using the `.groups` argument.
filter_df = filter_df %>%
  filter(p_correct < 0.8)
# View(filter_df)
## reader_3:0.70, reader_60:0.79, reader_76:0.72 , reader_256:0.71 , reader_262:0.57 

raw_df = df %>%
  mutate(word = str_trim(word)) %>%
  mutate(subj = str_remove(subj, "reader_")) %>%
  mutate(subj = as.integer(subj)) %>%
  filter(! subj %in% c(3, 60, 76, 256, 262)) %>%
  mutate(FPReg = if_else(total_duration == 0, NA, FPReg),
         first_duration = if_else(total_duration == 0, NA, first_duration),
         gaze_duration = if_else(total_duration == 0, NA, gaze_duration),
         go_past_time = if_else(total_duration == 0, NA, go_past_time),
         skip = if_else(total_duration == 0, 1, 0)) %>%
  # See below for filtering out reading measures that are super long
  dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration, gaze_duration, go_past_time, FPReg, skip, subj) #%>%
  # drop_na()

raw_df
# View(raw_df)
unique(raw_df$subj)
 [1]   1 100 101 102 103 104 105 107 108   2 255 259 261 263  53  54  55  56  57  58  61  62  63  64  65  68  70  71  72  73  74
[32]  75  79  80  82  83  88  89  90  91  93  94  96  97  98  99
length(unique(raw_df$subj))
[1] 46
# Average across subjects
motr_agg_df = raw_df %>%
  gather(metric, value, 6:11) %>%
    drop_na() %>%
    group_by(para_nr, word_nr, word, metric) %>%
    mutate(outlier = if_else(metric != "FPReg" & metric != "skip" & value > (mean(value) + 3 * sd(value) ), T, F)) %>%
    filter(outlier == F) %>%
  # # Filter out words with a reading-time of zero
  # mutate(zero = if_else(metric != "FPReg" & value == 0, T, F)) %>%
  # filter(zero == F) %>%
  drop_na() %>%
    summarise(value = mean(value),
              nsubj = length(unique(subj))) %>%
  ungroup() %>%
  arrange(para_nr, word_nr) %>%
  rename(
    text_id = para_nr,
    word_text_idx = word_nr,
    motr_value = value
  )
`summarise()` has grouped output by 'para_nr', 'word_nr', 'word'. You can override using the `.groups` argument.
motr_agg_df
# View(motr_agg_df)
# write.csv(motr_agg_df, file = "/Users/cui/Desktop/MoTR/pipeline/ancillary_data/motr_agg_df.csv", row.names = FALSE)

Comparison to Provo

# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("/Users/cui/Desktop/MoTR/pipeline/ancillary_data/provo_df.csv") %>%
  dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
  rename(word_idx = trigger_idx)

provo_modeling_df
# View(provo_modeling_df)
# Read in Provo eyetracking data

provo_raw_df = read.csv("/Users/cui/Desktop/MoTR/pipeline/ancillary_data/provo_eyetracking.csv")

# unique(provo_raw_df$Participant_ID)
# length(unique(provo_raw_df$Participant_ID))

provo_eyetracking_df = provo_raw_df %>%
  dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, Word, Word_Number, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT, IA_SKIP) %>%
  rename( #first_duration = IA_FIRST_FIXATION_DURATION,   
          gaze_duration = IA_FIRST_RUN_DWELL_TIME,
          total_duration = IA_DWELL_TIME,
          go_past_time = IA_REGRESSION_PATH_DURATION,
          subj = Participant_ID,
          text_id = Text_ID,
          sent_id = Sentence_Number,
          word_idx = Word_In_Sentence_Number,
          word_text_idx = Word_Number,   # IA_ID?
          word = Word,      # Word?
          FPReg = IA_REGRESSION_OUT,
          skip = IA_SKIP,
          ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>%
  mutate(first_duration = gaze_duration) %>%
  mutate(gaze_duration = if_else(ff_progressive == 0, 0, gaze_duration),
         go_past_time = if_else(ff_progressive == 0, 0, go_past_time)) %>%
  dplyr::select(-ff_progressive) %>%
  # drop_na() %>%     # will drop the whole row with all the metrics
  gather(metric, value, 7:12) %>%
  # mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
  # mutate(value = if_else(metric != "FPReg" & is.na(value), as.integer(0), as.integer(value))) %>%
  drop_na() %>%
  mutate(word = str_trim(word)) %>%
  mutate(subj = str_remove(subj, "Sub")) %>%
  mutate(subj = as.integer(subj)) %>%
    group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
    mutate(outlier = if_else(metric != "FPReg" & metric != "skip" & value > (mean(value) + 3 * sd(value) ), T, F)) %>%
    filter(outlier == F) %>%
  ungroup() #%>%
  # # Filter out words with a reading-time of zero
  # mutate(zero = if_else(metric != "FPReg" & value == 0,T, F)) %>%
  # filter(zero == F)

# Aggregate cross-participant data for all subjects
provo_eyetracking_agg_df = provo_eyetracking_df %>%
  group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
    summarise(value = mean(value),
              nsubj = length(unique(subj))) %>%
    ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)

# View(provo_eyetracking_agg_df)
# write.csv(provo_eyetracking_agg_df, file = "/Users/cui/Desktop/MoTR/pipeline/ancillary_data/provo_eyetracking_agg_df.csv", row.names = FALSE)

# Split the eyetracking data in two by subjects to see how well it correlates with itself
provo_eyetracking_subj1_df_temp = provo_eyetracking_df %>%
  filter(subj <= 42) %>%
  mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
  group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
    summarise(value = mean(value)) %>%
  ungroup() %>%
  rename(value_1 = value) #%>%
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
  # dplyr::select(-sent_id, -word_idx)

# View(provo_eyetracking_subj1_df_temp)

provo_eyetracking_subj1_df = merge(provo_eyetracking_subj1_df_temp, motr_agg_df, by=c("text_id", "word_text_idx", "metric")) %>%
  arrange(text_id, sent_id, word_idx) %>%
  filter(!(text_id == 13 & word_text_idx >= 20 & word_text_idx <= 52)) %>%
  filter(!(text_id == 3 & word_text_idx >= 46 & word_text_idx <= 57)) %>%
  rename(word = word.y) %>%
  dplyr::select(text_id, word_text_idx, metric, word, value_1)

# View(provo_eyetracking_subj1_df)

provo_eyetracking_subj2_df = provo_eyetracking_df %>%
  filter(subj > 42) %>%
  mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
  group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
    summarise(value = mean(value)) %>%
  ungroup() %>%
    rename(value_2 = value)%>%
  dplyr::select(-sent_id, -word_idx)
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_subj2_df)
  
provo_eyetr_grouped_df = merge(provo_eyetracking_subj2_df, provo_eyetracking_subj1_df, by=c("text_id", "word_text_idx", "metric")) %>%
  # filter(word.x == word.y) %>%
  dplyr::select(-word.y) %>%
  group_by(metric) %>%
    mutate(motr_outlier = if_else(metric != "FPReg" & metric != "skip" & value_1 > (mean(value_1) + 3 * sd(value_1) ), T, F)) %>%
    filter(motr_outlier == F) %>%
    mutate(eyetr_outlier = if_else(metric != "FPReg" & metric != "skip" & value_2 > (mean(value_2) + 3 * sd(value_2) ), T, F)) %>%
    filter(eyetr_outlier == F) %>%
  ungroup() %>%
  gather(measure, value, c("value_1", "value_2")) %>%
  dplyr::select(-motr_outlier, -eyetr_outlier)

# View(provo_eyetr_grouped_df)
provo_df = merge(provo_eyetracking_agg_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
  mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
  arrange(text_id, sent_id, word_idx) %>%
  rename(eyetr_value = value) 

provo_df = merge(provo_df, motr_agg_df, by=c("text_id", "word_text_idx", "metric")) %>%
arrange(text_id, sent_id, word_idx) %>%
  # almost all the word.x != word.y is because of normalization problem, so we can keep them, instead, deleting some special cases
filter(!(text_id == 13 & word_text_idx >= 20 & word_text_idx <= 52)) %>%
  filter(!(text_id == 3 & word_text_idx >= 46 & word_text_idx <= 57)) %>%
# filter(word.x == word) #%>%
dplyr::select(-word.x, -word.y) %>%
group_by(metric) %>%
  mutate(motr_outlier = if_else(metric != "FPReg" & motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
  filter(motr_outlier == F) %>%
  mutate(eyetr_outlier = if_else(metric != "FPReg" & eyetr_value > (mean(eyetr_value) + 3 * sd(eyetr_value) ), T, F)) %>%
  filter(eyetr_outlier == F) %>%
ungroup() %>%
gather(measure, value, c("eyetr_value", "motr_value")) %>%
dplyr::select(-motr_outlier, -eyetr_outlier)
  
# View(provo_df)
# provo_df
provo_df %>%
  mutate(measure = if_else(measure == "eyetr_value", "Eyetracking Value", "MoTR Value")) %>%
  filter(metric != "FPReg" & metric != "skip") %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    facet_wrap(.~measure, scales="free_y") +
    xlab("Reading Time (ms)")


# ggsave("../visualization/density.png", device = "png", width = 6, height = 2.5)
print("Gaze Duration")
[1] "Gaze Duration"
gd_df = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
print(cor.test(gd_df$eyetr_value, gd_df$motr_value)$estimate)
   cor 
0.4822 
cor_df = provo_eyetr_grouped_df %>% filter(metric == "gaze_duration") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.6391 
print("First Duration")
[1] "First Duration"
fd_df = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
print(cor.test(fd_df$eyetr_value, fd_df$motr_value)$estimate)
   cor 
0.4536 
# line 489, 490; 3147, 3148 share the same key
cor_df = provo_eyetr_grouped_df %>% filter(metric == "first_duration") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.8981 
print("Go Past Time")
[1] "Go Past Time"
gpt_df = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
print(cor.test(gpt_df$eyetr_value, gpt_df$motr_value)$estimate)
   cor 
0.3709 
cor_df = provo_eyetr_grouped_df %>% filter(metric == "go_past_time") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.6573 
print("Total Duration")
[1] "Total Duration"
td_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
print(cor.test(td_df$eyetr_value, td_df$motr_value)$estimate)
   cor 
0.7601 
cor_df = provo_eyetr_grouped_df %>% filter(metric == "total_duration") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.9371 
print("Regression")
[1] "Regression"
reg_df = provo_df %>% filter(metric == "FPReg") %>% spread(measure, value)
print(cor.test(reg_df$eyetr_value, reg_df$motr_value)$estimate)
   cor 
0.2454 
cor_df = provo_eyetr_grouped_df %>% filter(metric == "FPReg") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.5558 
print("Skip")
[1] "Skip"
skip_df = provo_df %>% filter(metric == "skip") %>% spread(measure, value)
print(cor.test(skip_df$eyetr_value, skip_df$motr_value)$estimate)
  cor 
0.757 
cor_df = provo_eyetr_grouped_df %>% filter(metric == "skip") %>% group_by(text_id, metric, measure) %>%
  summarize(value = mean(value, na.rm = TRUE), .groups = 'drop') %>% spread(measure, value)
print(cor.test(cor_df$value_1, cor_df$value_2)$estimate)
   cor 
0.7544 

provo_df %>%
  filter(metric != "FPReg" & metric != "skip") %>%
  spread(measure, value) %>%
  ggplot(aes(x = motr_value, y=eyetr_value)) +
    geom_point(alpha = 0.05) +
    geom_abline(slope=1, intercept=0, color = "black") +
    #stat_summary_bin(bins=100, fun.data = "mean_cl_boot", size = 0.05) +
    facet_wrap(.~metric, scales = "free", nrow = 1) +
    coord_cartesian(ylim=c(0, 500), xlim=c(0, 500)) +
    geom_smooth()



# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)

Correlations to Word-Level Statistical Properties

print("Len")
[1] "Len"
cor_df = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$len)$estimate)
   cor 
0.7144 
print(cor.test(cor_df$motr_value, cor_df$len)$estimate)
   cor 
0.6182 
print("Freq")
[1] "Freq"
cor_df = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$freq)$estimate)
    cor 
-0.6649 
print(cor.test(cor_df$motr_value, cor_df$freq)$estimate)
    cor 
-0.5246 
print("Surp")
[1] "Surp"
cor_df = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$surp)$estimate)
   cor 
0.4676 
print(cor.test(cor_df$motr_value, cor_df$surp)$estimate)
   cor 
0.3558 
print("Len")
[1] "Len"
cor_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$len)$estimate)
  cor 
0.825 
print(cor.test(cor_df$motr_value, cor_df$len)$estimate)
  cor 
0.838 
print("Freq")
[1] "Freq"
cor_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$freq)$estimate)
   cor 
-0.781 
print(cor.test(cor_df$motr_value, cor_df$freq)$estimate)
    cor 
-0.7258 
print("Surp")
[1] "Surp"
cor_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$surp)$estimate)
   cor 
0.5931 
print(cor.test(cor_df$motr_value, cor_df$surp)$estimate)
   cor 
0.5104 
print("Len")
[1] "Len"
cor_df = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$len)$estimate)
   cor 
0.6562 
print(cor.test(cor_df$motr_value, cor_df$len)$estimate)
   cor 
0.6062 
print("Freq")
[1] "Freq"
cor_df = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$freq)$estimate)
    cor 
-0.6242 
print(cor.test(cor_df$motr_value, cor_df$freq)$estimate)
    cor 
-0.5118 
print("Surp")
[1] "Surp"
cor_df = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$surp)$estimate)
   cor 
0.4836 
print(cor.test(cor_df$motr_value, cor_df$surp)$estimate)
   cor 
0.3692 
print("Len")
[1] "Len"
cor_df = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$len)$estimate)
   cor 
0.6314 
print(cor.test(cor_df$motr_value, cor_df$len)$estimate)
   cor 
0.4132 
print("Freq")
[1] "Freq"
cor_df = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$freq)$estimate)
   cor 
-0.575 
print(cor.test(cor_df$motr_value, cor_df$freq)$estimate)
    cor 
-0.3696 
print("Surp")
[1] "Surp"
cor_df = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
print(cor.test(cor_df$eyetr_value, cor_df$surp)$estimate)
   cor 
0.4482 
print(cor.test(cor_df$motr_value, cor_df$surp)$estimate)
   cor 
0.2663 
provo_df %>%
  gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
  mutate(measure = if_else(measure == "eyetr_value", "Eyetracking Value", "MoTR Value")) %>%
  mutate(word_prop = case_when(
    word_prop == "freq" ~ "Frequency",
    word_prop == "len" ~ "Length",
    word_prop == "surp" ~ "Surprisal"
  )) %>%
  filter(metric == "gaze_duration") %>%
  ggplot(aes(x = value, y=word_prop_val, color = measure)) +
    geom_point(alpha = 0.1) +
    facet_wrap(measure~word_prop, scales="free", strip.position = "right") +
    geom_smooth(color = "grey") +
    xlab("Reading Measure") +
  theme(
    legend.position = "none",
    strip.placement = "outside"
  )


# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
  ggplot(aes(x = value, y=freq, color=metric)) +
    geom_point(alpha = 0.1) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

provo_df %>%
  ggplot(aes(x = value, y=surp, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

Shape of surprisal / RT relationship


fit_gam_inner = function(bootstrap_sample, mean_predictors) {
  
  df = bootstrap_sample$data
  weights = tabulate(as.integer(bootstrap_sample), nrow(df))
  
  m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
  terms_to_predict = c("s(surp)", "s(prev_surp)")
  
  newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
                       #surp=mean_predictors$surp, prev_surp=seq(0,20,by=0.1),
                       freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
                       len=mean_predictors$len, prev_len=mean_predictors$len)
                       # len=mean_predictors$freq, prev_len=mean_predictors$freq) ##????

  # Returns a matrix N_samples * N_terms.
  per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")

  # Additive model -- sum across predictor response contributions (matrix columns).
  predictions = rowSums(per_term_predictions)
  
  # print("Fitted GAM model:")
  # print(m)

  # print("Predicted values for specified smooth terms:")
  # print(per_term_predictions)

  return(newdata %>% mutate(y=predictions))
}

fit_gam = function(df, mean_predictors, alpha=0.05) {
  # Bootstrap-resample data
  boot_models = df %>% bootstraps(times=10) %>% 
   # Fit a GAM and get predictions for each sample
    mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
  
  # Extract mean and 5% and 95% percentile y-values for each surprisal value
  result = boot_models %>% 
    unnest(smoothed) %>% 
    dplyr::select(surp, y) %>%
    # dplyr::select(prev_surp, y) %>%
    group_by(surp) %>%
    # group_by(prev_surp) %>%
      summarise(y_lower=quantile(y, alpha / 2), 
                y_upper=quantile(y, 1 - alpha / 2),
                y=mean(y)) %>% 
    ungroup()
  # print(result)
  
  return (result)
}

gam_modeling_df = provo_df %>%
  spread(measure, value) %>%
  # mutate(len = nchar(word)) %>%  # len has already exists, but do not count punct into len.
  group_by(metric, text_id) %>%
    arrange(word_text_idx) %>%
    mutate(prev_surp = lag(surp),
           prev_freq = lag(freq),
           prev_len = lag(len),
           prev_eyetr_value = lag(eyetr_value)) %>%
  ungroup() %>%
  drop_na() %>%
  rename(psychometric = motr_value)
# View(gam_modeling_df)

smooths_df = data.frame()

metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
# metrics = c("gaze_duration")
for (m in metrics) {
  print(paste0("Fitting model for ", m))
  dummy_df = gam_modeling_df %>% filter(metric == m)
  mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
  smooths = dummy_df %>% fit_gam(., mean_predictors)
  #Fix 0 surprisal = 0 ms
  gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
  smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
  # View(smooths_df)
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"

Get Density Data

get_d_points = function(df) {
    x = density(df$surp)$x
    y = density(df$surp)$y
    return(data.frame(x, y))
  }

density_data = data.frame()

for(m in c("gaze_duration", "total_duration", "go_past_time", "first_duration")) {
  dummy_df = provo_df %>% filter(metric == m) %>%
      do({get_d_points(.)}) %>%
      filter(x>0, x<20)
  density_data = rbind(density_data, dummy_df %>% mutate(metric=m))
}

# Surprisal curves
  ggplot() +
      # Density Data
      annotate("rect", xmin=0, xmax=20, ymin=-20,ymax=-10, fill="#f4f4f4", color="grey", alpha=1, size = 0) +
      geom_line(data = density_data, aes(x=x, y=y*50 - 18), color="#aaaaaa", size = 0.4) +
      # Surrp / Rt data
      # geom_line(data = smooths_df, aes(x=prev_surp, y=y, color = psychometric), size=0.7) +
      geom_line(data = smooths_df, aes(x=surp, y=y, color = psychometric), size=0.7) +
      # geom_ribbon(data = smooths_df, aes(x=prev_surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
      geom_ribbon(data = smooths_df, aes(x=surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
      scale_x_continuous(labels=c(0, 10, 20), breaks=c(0, 10, 20), minor_breaks = NULL) +
      facet_wrap(psychometric~., nrow = 1) +
      ylab("Slowdown due to Surprisal (ms)") +
      xlab("Surprisal of Word") +
      # ggtitle("MoTR Times and Previous Word Surprisal")
      ggtitle("MoTR Times and Current Word Surprisal")

  theme(
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
List of 2
 $ legend.position : chr "none"
 $ panel.grid.minor: list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE
  
# ggsave("../visualization/surprisal_rt_link.png", device = "png", width = 6, height = 2.5)
fit_gam_inner_2 = function(bootstrap_sample, mean_predictors) {
  
  df = bootstrap_sample$data
  weights = tabulate(as.integer(bootstrap_sample), nrow(df))
  
  m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
  terms_to_predict = c("s(surp)", "s(prev_surp)")
  
  newdata = data.frame(surp=mean_predictors$surp, prev_surp=seq(0,20,by=0.1),
                       freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
                       len=mean_predictors$len, prev_len=mean_predictors$len)

  # Returns a matrix N_samples * N_terms.
  per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")

  # Additive model -- sum across predictor response contributions (matrix columns).
  predictions = rowSums(per_term_predictions)

  return(newdata %>% mutate(y=predictions))
}

fit_gam_2 = function(df, mean_predictors, alpha=0.05) {
  # Bootstrap-resample data
  boot_models = df %>% bootstraps(times=10) %>% 
   # Fit a GAM and get predictions for each sample
    mutate(smoothed=map(splits, fit_gam_inner_2, mean_predictors=mean_predictors))
  
  # Extract mean and 5% and 95% percentile y-values for each surprisal value
  result = boot_models %>% 
    unnest(smoothed) %>% 
    # dplyr::select(surp, y) %>%
    dplyr::select(prev_surp, y) %>%
    # group_by(surp) %>%
    group_by(prev_surp) %>%
      summarise(y_lower=quantile(y, alpha / 2), 
                y_upper=quantile(y, 1 - alpha / 2),
                y=mean(y)) %>% 
    ungroup()
  # print(result)
  
  return (result)
}
gam_modeling_df_2 = provo_df %>%
  spread(measure, value) %>%
  # mutate(len = nchar(word)) %>%  # len has already exists, but do not count punct into len.
  group_by(metric, text_id) %>%
    arrange(word_text_idx) %>%
    mutate(prev_surp = lag(surp),
           prev_freq = lag(freq),
           prev_len = lag(len),
           prev_eyetr_value = lag(eyetr_value)) %>%
  ungroup() %>%
  drop_na() %>%
  rename(psychometric = motr_value)

smooths_df = data.frame()

metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
  print(paste0("Fitting model for ", m))
  dummy_df = gam_modeling_df_2 %>% filter(metric == m)
  mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
  smooths = dummy_df %>% fit_gam_2(., mean_predictors)
  #Fix 0 surprisal = 0 ms
  gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
  smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"
get_d_points = function(df) {
    x = density(df$surp)$x
    y = density(df$surp)$y
    return(data.frame(x, y))
  }

density_data = data.frame()

for(m in c("gaze_duration", "total_duration", "go_past_time", "first_duration")) {
  dummy_df = provo_df %>% filter(metric == m) %>%
      do({get_d_points(.)}) %>%
      filter(x>0, x<20)
  density_data = rbind(density_data, dummy_df %>% mutate(metric=m))
}
# Surprisal curves
  ggplot() +
      # Density Data
      annotate("rect", xmin=0, xmax=20, ymin=-20,ymax=-10, fill="#f4f4f4", color="grey", alpha=1, size = 0) +
      geom_line(data = density_data, aes(x=x, y=y*50 - 18), color="#aaaaaa", size = 0.4) +
      # Surrp / Rt data
      geom_line(data = smooths_df, aes(x=prev_surp, y=y, color = psychometric), size=0.7) +
      geom_ribbon(data = smooths_df, aes(x=prev_surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
      scale_x_continuous(labels=c(0, 10, 20), breaks=c(0, 10, 20), minor_breaks = NULL) +
      facet_wrap(psychometric~., nrow = 1) +
      ylab("Slowdown due to Surprisal (ms)") +
      xlab("Surprisal of Word") +
      ggtitle("MoTR Times and Previous Word Surprisal")

  theme(
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
List of 2
 $ legend.position : chr "none"
 $ panel.grid.minor: list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

precision and recall for FPReg

FPReg_df = provo_df %>% filter(metric == "FPReg") %>% spread(measure, value)
# write.csv(FPReg_df, file = "/Users/cui/Desktop/MoTR/pipeline/ancillary_data/FPReg.csv", row.names = FALSE)
confusion_matrix <- table(FPReg_df$motr_value > 0, FPReg_df$eyetr_value > 0)
confusion_matrix
       
        FALSE TRUE
  FALSE    79 1514
  TRUE     22  918
true_positives <- confusion_matrix[2, 2]
false_positives <- confusion_matrix[2, 1]
false_negatives <- confusion_matrix[1, 2]

precision <- true_positives / (true_positives + false_positives)
recall <- true_positives / (true_positives + false_negatives)

print("precision of Motr FPReg:")
[1] "precision of Motr FPReg:"
print(precision)
[1] 0.9766
print("Recall of Motr FPReg:")
[1] "Recall of Motr FPReg:"
print(recall)
[1] 0.3775

Relationship between first duration and regression probabilities. –> exploratory, not finished.

# Motr --> fit a linear model for first_duration. finding: More FPReg, more first duration. 

rlt_df = provo_df %>% filter(metric == "FPReg" | metric == "first_duration") %>% 
  filter(surp > 10) %>%
  spread(metric, value) %>%
  drop_na()

View(rlt_df)

motr_rlt = rlt_df %>% filter(measure == "motr_value") %>%
  mutate(FPReg_bi = if_else(FPReg == 0, 0, 1),
         fd_normalized = first_duration / len)

motr_rlt$FPReg_bi <- factor(motr_rlt$FPReg_bi)

View(motr_rlt)

# levels(motr_rlt$FPReg_bi)

# motr_lm = lm(first_duration ~ freq + FPReg_bi, data = motr_rlt)
motr_lm = lm(fd_normalized ~ FPReg, data = motr_rlt)

summary(motr_lm)

Call:
lm(formula = fd_normalized ~ FPReg, data = motr_rlt)

Residuals:
   Min     1Q Median     3Q    Max 
-41.21 -22.33  -9.31  11.75 100.63 

Coefficients:
            Estimate Std. Error t value            Pr(>|t|)    
(Intercept)    84.75       3.67   23.08 <0.0000000000000002 ***
FPReg         -44.00      27.22   -1.62                0.11    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 31.9 on 101 degrees of freedom
Multiple R-squared:  0.0252,    Adjusted R-squared:  0.0156 
F-statistic: 2.61 on 1 and 101 DF,  p-value: 0.109
# write.csv(rlt_df, file = "/Users/cui/Desktop/MoTR/pipeline/ancillary_data/rlt.csv", row.names = FALSE)
# eyrtr -> same as motr, more FPReg, more first_duration

eyetr_rlt = rlt_df %>% filter(measure == "eyetr_value") %>%
  mutate(FPReg_bi = if_else(FPReg == 0, 0, 1),
         fd_normalized = first_duration / len)

eyetr_rlt$FPReg_bi <- factor(eyetr_rlt$FPReg_bi)

# eyetr_lm = lm(first_duration ~ FPReg_bi, data = eyetr_rlt)
eyetr_lm = lm(fd_normalized ~ FPReg, data = eyetr_rlt)

summary(eyetr_lm)

Call:
lm(formula = fd_normalized ~ FPReg, data = eyetr_rlt)

Residuals:
   Min     1Q Median     3Q    Max 
-27.93 -15.95  -4.94   7.02  81.22 

Coefficients:
            Estimate Std. Error t value            Pr(>|t|)    
(Intercept)    52.46       3.57   14.70 <0.0000000000000002 ***
FPReg           6.83      18.89    0.36                0.72    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 22.3 on 101 degrees of freedom
Multiple R-squared:  0.00129,   Adjusted R-squared:  -0.0086 
F-statistic: 0.131 on 1 and 101 DF,  p-value: 0.718
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0Kc2hoaCA8LSBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMgIyBJdCdzIGEgbGlicmFyeSwgc28gc2hoaCEKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCm9wdGlvbnMoZGlnaXRzPTQpCm9wdGlvbnMoc2NpcGVuPTk5OSkKc2V0LnNlZWQoNDQ0KQpwaXBlX21lc3NhZ2UgPSBmdW5jdGlvbiguZGF0YSwgc3RhdHVzKSB7bWVzc2FnZShzdGF0dXMpOyAuZGF0YX0KCmBgYAoKCiMgUmVhZCBpbiBNb1RSIERhdGEKCmBgYHtyfQoKcmF0ZSA9IDE2MAoKZmlsZV9wcmVmaXggPSAiL1VzZXJzL2N1aS9EZXNrdG9wL21vdHJfcHJvdm9fMTYwLyIKZm5hbWVzID0gbGlzdC5maWxlcyhwYXRoPWZpbGVfcHJlZml4KQoKZGYgPSBkYXRhLmZyYW1lKCkKZm9yIChmIGluIGZuYW1lcykgewogIHRlbXAgPSByZWFkLmNzdihwYXN0ZTAoZmlsZV9wcmVmaXgsICIvIiwgZikpICU+JQogICAgbXV0YXRlKHN1YmogPSBzdHJfcmVtb3ZlKGYsICJfcmVhZGluZ19tZWFzdXJlcy5jc3YiKSkgJT4lCiAgICByZW5hbWUoZ29fcGFzdF90aW1lID0gZ29fcGFzc190aW1lKQogIGRmID0gcmJpbmQoZGYsIHRlbXApCn0KCiMgVmlldyhkZikKCmZpbHRlcl9kZiA9IGRmICU+JQogIGdyb3VwX2J5KHBhcmFfbnIsIHN1YmopICU+JQogICAgc3VtbWFyaXNlKGNvcnJlY3QgPSBpZl9lbHNlKHVuaXF1ZShjb3JyZWN0bmVzcykgPT0gMSwgMSwgMCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgZ3JvdXBfYnkoc3ViaikgJT4lCiAgICBzdW1tYXJpc2UocF9jb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShwX2NvcnJlY3QgPSByb3VuZChwX2NvcnJlY3QsIGRpZ2l0cyA9IDIpKQoKZmlsdGVyX2RmID0gZmlsdGVyX2RmICU+JQogIGZpbHRlcihwX2NvcnJlY3QgPCAwLjgpCiMgVmlldyhmaWx0ZXJfZGYpCiMjIHJlYWRlcl8zOjAuNzAsIHJlYWRlcl82MDowLjc5LCByZWFkZXJfNzY6MC43MiAsIHJlYWRlcl8yNTY6MC43MSAsIHJlYWRlcl8yNjI6MC41NyAKCnJhd19kZiA9IGRmICU+JQogIG11dGF0ZSh3b3JkID0gc3RyX3RyaW0od29yZCkpICU+JQogIG11dGF0ZShzdWJqID0gc3RyX3JlbW92ZShzdWJqLCAicmVhZGVyXyIpKSAlPiUKICBtdXRhdGUoc3ViaiA9IGFzLmludGVnZXIoc3ViaikpICU+JQogIGZpbHRlcighIHN1YmogJWluJSBjKDMsIDYwLCA3NiwgMjU2LCAyNjIpKSAlPiUKICBtdXRhdGUoRlBSZWcgPSBpZl9lbHNlKHRvdGFsX2R1cmF0aW9uID09IDAsIE5BLCBGUFJlZyksCiAgICAgICAgIGZpcnN0X2R1cmF0aW9uID0gaWZfZWxzZSh0b3RhbF9kdXJhdGlvbiA9PSAwLCBOQSwgZmlyc3RfZHVyYXRpb24pLAogICAgICAgICBnYXplX2R1cmF0aW9uID0gaWZfZWxzZSh0b3RhbF9kdXJhdGlvbiA9PSAwLCBOQSwgZ2F6ZV9kdXJhdGlvbiksCiAgICAgICAgIGdvX3Bhc3RfdGltZSA9IGlmX2Vsc2UodG90YWxfZHVyYXRpb24gPT0gMCwgTkEsIGdvX3Bhc3RfdGltZSksCiAgICAgICAgIHNraXAgPSBpZl9lbHNlKHRvdGFsX2R1cmF0aW9uID09IDAsIDEsIDApKSAlPiUKICAjIFNlZSBiZWxvdyBmb3IgZmlsdGVyaW5nIG91dCByZWFkaW5nIG1lYXN1cmVzIHRoYXQgYXJlIHN1cGVyIGxvbmcKICBkcGx5cjo6c2VsZWN0KGV4cHJfaWQsIGNvbmRfaWQsIHBhcmFfbnIsIHdvcmQsIHdvcmRfbnIsIGZpcnN0X2R1cmF0aW9uLCB0b3RhbF9kdXJhdGlvbiwgZ2F6ZV9kdXJhdGlvbiwgZ29fcGFzdF90aW1lLCBGUFJlZywgc2tpcCwgc3ViaikgIyU+JQogICMgZHJvcF9uYSgpCgpyYXdfZGYKIyBWaWV3KHJhd19kZikKCmBgYAoKYGBge3J9CnVuaXF1ZShyYXdfZGYkc3ViaikKbGVuZ3RoKHVuaXF1ZShyYXdfZGYkc3ViaikpCmBgYAoKCgpgYGB7cn0KIyBBdmVyYWdlIGFjcm9zcyBzdWJqZWN0cwptb3RyX2FnZ19kZiA9IHJhd19kZiAlPiUKICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgNjoxMSkgJT4lCiAgICBkcm9wX25hKCkgJT4lCiAgICBncm91cF9ieShwYXJhX25yLCB3b3JkX25yLCB3b3JkLCBtZXRyaWMpICU+JQogICAgbXV0YXRlKG91dGxpZXIgPSBpZl9lbHNlKG1ldHJpYyAhPSAiRlBSZWciICYgbWV0cmljICE9ICJza2lwIiAmIHZhbHVlID4gKG1lYW4odmFsdWUpICsgMyAqIHNkKHZhbHVlKSApLCBULCBGKSkgJT4lCiAgICBmaWx0ZXIob3V0bGllciA9PSBGKSAlPiUKICAjICMgRmlsdGVyIG91dCB3b3JkcyB3aXRoIGEgcmVhZGluZy10aW1lIG9mIHplcm8KICAjIG11dGF0ZSh6ZXJvID0gaWZfZWxzZShtZXRyaWMgIT0gIkZQUmVnIiAmIHZhbHVlID09IDAsIFQsIEYpKSAlPiUKICAjIGZpbHRlcih6ZXJvID09IEYpICU+JQogIGRyb3BfbmEoKSAlPiUKICAgIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpLAogICAgICAgICAgICAgIG5zdWJqID0gbGVuZ3RoKHVuaXF1ZShzdWJqKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKHBhcmFfbnIsIHdvcmRfbnIpICU+JQogIHJlbmFtZSgKICAgIHRleHRfaWQgPSBwYXJhX25yLAogICAgd29yZF90ZXh0X2lkeCA9IHdvcmRfbnIsCiAgICBtb3RyX3ZhbHVlID0gdmFsdWUKICApCgptb3RyX2FnZ19kZgojIFZpZXcobW90cl9hZ2dfZGYpCiMgd3JpdGUuY3N2KG1vdHJfYWdnX2RmLCBmaWxlID0gIi9Vc2Vycy9jdWkvRGVza3RvcC9Nb1RSL3BpcGVsaW5lL2FuY2lsbGFyeV9kYXRhL21vdHJfYWdnX2RmLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKYGBgCgoKCgojIENvbXBhcmlzb24gdG8gUHJvdm8KCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIHN1cnByaXNhbCwgZnJlcXVlbmN5IGFuZCBsZW5ndGggZGF0YQpwcm92b19tb2RlbGluZ19kZiA9IHJlYWQuY3N2KCIvVXNlcnMvY3VpL0Rlc2t0b3AvTW9UUi9waXBlbGluZS9hbmNpbGxhcnlfZGF0YS9wcm92b19kZi5jc3YiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHRleHRfaWQsIHNlbnRfaWQsIHRyaWdnZXJfaWR4LCB3b3JkLCBmcmVxLCBzdXJwLCBsZW4pICU+JQogIHJlbmFtZSh3b3JkX2lkeCA9IHRyaWdnZXJfaWR4KQoKcHJvdm9fbW9kZWxpbmdfZGYKIyBWaWV3KHByb3ZvX21vZGVsaW5nX2RmKQoKYGBgCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIGV5ZXRyYWNraW5nIGRhdGEKCnByb3ZvX3Jhd19kZiA9IHJlYWQuY3N2KCIvVXNlcnMvY3VpL0Rlc2t0b3AvTW9UUi9waXBlbGluZS9hbmNpbGxhcnlfZGF0YS9wcm92b19leWV0cmFja2luZy5jc3YiKQoKIyB1bmlxdWUocHJvdm9fcmF3X2RmJFBhcnRpY2lwYW50X0lEKQojIGxlbmd0aCh1bmlxdWUocHJvdm9fcmF3X2RmJFBhcnRpY2lwYW50X0lEKSkKCnByb3ZvX2V5ZXRyYWNraW5nX2RmID0gcHJvdm9fcmF3X2RmICU+JQogIGRwbHlyOjpzZWxlY3QoUGFydGljaXBhbnRfSUQsIFRleHRfSUQsIFNlbnRlbmNlX051bWJlciwgV29yZF9Jbl9TZW50ZW5jZV9OdW1iZXIsIFdvcmQsIFdvcmRfTnVtYmVyLCBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUsIElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLCBJQV9EV0VMTF9USU1FLCBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sIElBX1JFR1JFU1NJT05fT1VULCBJQV9TS0lQKSAlPiUKICByZW5hbWUoICNmaXJzdF9kdXJhdGlvbiA9IElBX0ZJUlNUX0ZJWEFUSU9OX0RVUkFUSU9OLCAgIAogICAgICAgICAgZ2F6ZV9kdXJhdGlvbiA9IElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLAogICAgICAgICAgdG90YWxfZHVyYXRpb24gPSBJQV9EV0VMTF9USU1FLAogICAgICAgICAgZ29fcGFzdF90aW1lID0gSUFfUkVHUkVTU0lPTl9QQVRIX0RVUkFUSU9OLAogICAgICAgICAgc3ViaiA9IFBhcnRpY2lwYW50X0lELAogICAgICAgICAgdGV4dF9pZCA9IFRleHRfSUQsCiAgICAgICAgICBzZW50X2lkID0gU2VudGVuY2VfTnVtYmVyLAogICAgICAgICAgd29yZF9pZHggPSBXb3JkX0luX1NlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfdGV4dF9pZHggPSBXb3JkX051bWJlciwgICAjIElBX0lEPwogICAgICAgICAgd29yZCA9IFdvcmQsICAgICAgIyBXb3JkPwogICAgICAgICAgRlBSZWcgPSBJQV9SRUdSRVNTSU9OX09VVCwKICAgICAgICAgIHNraXAgPSBJQV9TS0lQLAogICAgICAgICAgZmZfcHJvZ3Jlc3NpdmUgPSBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUpICU+JQogIG11dGF0ZShmaXJzdF9kdXJhdGlvbiA9IGdhemVfZHVyYXRpb24pICU+JQogIG11dGF0ZShnYXplX2R1cmF0aW9uID0gaWZfZWxzZShmZl9wcm9ncmVzc2l2ZSA9PSAwLCAwLCBnYXplX2R1cmF0aW9uKSwKICAgICAgICAgZ29fcGFzdF90aW1lID0gaWZfZWxzZShmZl9wcm9ncmVzc2l2ZSA9PSAwLCAwLCBnb19wYXN0X3RpbWUpKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1mZl9wcm9ncmVzc2l2ZSkgJT4lCiAgIyBkcm9wX25hKCkgJT4lICAgICAjIHdpbGwgZHJvcCB0aGUgd2hvbGUgcm93IHdpdGggYWxsIHRoZSBtZXRyaWNzCiAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIDc6MTIpICU+JQogICMgbXV0YXRlKHZhbHVlID0gaWZfZWxzZShpcy5uYSh2YWx1ZSksIGFzLmludGVnZXIoMCksIGFzLmludGVnZXIodmFsdWUpKSkgJT4lCiAgIyBtdXRhdGUodmFsdWUgPSBpZl9lbHNlKG1ldHJpYyAhPSAiRlBSZWciICYgaXMubmEodmFsdWUpLCBhcy5pbnRlZ2VyKDApLCBhcy5pbnRlZ2VyKHZhbHVlKSkpICU+JQogIGRyb3BfbmEoKSAlPiUKICBtdXRhdGUod29yZCA9IHN0cl90cmltKHdvcmQpKSAlPiUKICBtdXRhdGUoc3ViaiA9IHN0cl9yZW1vdmUoc3ViaiwgIlN1YiIpKSAlPiUKICBtdXRhdGUoc3ViaiA9IGFzLmludGVnZXIoc3ViaikpICU+JQogICAgZ3JvdXBfYnkodGV4dF9pZCwgd29yZF90ZXh0X2lkeCwgc2VudF9pZCwgd29yZF9pZHgsIHdvcmQsIG1ldHJpYykgJT4lCiAgICBtdXRhdGUob3V0bGllciA9IGlmX2Vsc2UobWV0cmljICE9ICJGUFJlZyIgJiBtZXRyaWMgIT0gInNraXAiICYgdmFsdWUgPiAobWVhbih2YWx1ZSkgKyAzICogc2QodmFsdWUpICksIFQsIEYpKSAlPiUKICAgIGZpbHRlcihvdXRsaWVyID09IEYpICU+JQogIHVuZ3JvdXAoKSAjJT4lCiAgIyAjIEZpbHRlciBvdXQgd29yZHMgd2l0aCBhIHJlYWRpbmctdGltZSBvZiB6ZXJvCiAgIyBtdXRhdGUoemVybyA9IGlmX2Vsc2UobWV0cmljICE9ICJGUFJlZyIgJiB2YWx1ZSA9PSAwLFQsIEYpKSAlPiUKICAjIGZpbHRlcih6ZXJvID09IEYpCgojIEFnZ3JlZ2F0ZSBjcm9zcy1wYXJ0aWNpcGFudCBkYXRhIGZvciBhbGwgc3ViamVjdHMKcHJvdm9fZXlldHJhY2tpbmdfYWdnX2RmID0gcHJvdm9fZXlldHJhY2tpbmdfZGYgJT4lCiAgZ3JvdXBfYnkodGV4dF9pZCwgd29yZF90ZXh0X2lkeCwgc2VudF9pZCwgd29yZF9pZHgsIHdvcmQsIG1ldHJpYykgJT4lCiAgICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgICBuc3ViaiA9IGxlbmd0aCh1bmlxdWUoc3ViaikpKSAlPiUKICAgIHVuZ3JvdXAoKQoKIyBWaWV3KHByb3ZvX2V5ZXRyYWNraW5nX2RmKQoKIyBWaWV3KHByb3ZvX2V5ZXRyYWNraW5nX2FnZ19kZikKIyB3cml0ZS5jc3YocHJvdm9fZXlldHJhY2tpbmdfYWdnX2RmLCBmaWxlID0gIi9Vc2Vycy9jdWkvRGVza3RvcC9Nb1RSL3BpcGVsaW5lL2FuY2lsbGFyeV9kYXRhL3Byb3ZvX2V5ZXRyYWNraW5nX2FnZ19kZi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKYGBge3J9CgojIFNwbGl0IHRoZSBleWV0cmFja2luZyBkYXRhIGluIHR3byBieSBzdWJqZWN0cyB0byBzZWUgaG93IHdlbGwgaXQgY29ycmVsYXRlcyB3aXRoIGl0c2VsZgpwcm92b19leWV0cmFja2luZ19zdWJqMV9kZl90ZW1wID0gcHJvdm9fZXlldHJhY2tpbmdfZGYgJT4lCiAgZmlsdGVyKHN1YmogPD0gNDIpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGdyb3VwX2J5KHRleHRfaWQsIHdvcmRfdGV4dF9pZHgsIHNlbnRfaWQsIHdvcmRfaWR4LCB3b3JkLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICByZW5hbWUodmFsdWVfMSA9IHZhbHVlKSAjJT4lCiAgIyBkcGx5cjo6c2VsZWN0KC1zZW50X2lkLCAtd29yZF9pZHgpCgojIFZpZXcocHJvdm9fZXlldHJhY2tpbmdfc3ViajFfZGZfdGVtcCkKCnByb3ZvX2V5ZXRyYWNraW5nX3N1YmoxX2RmID0gbWVyZ2UocHJvdm9fZXlldHJhY2tpbmdfc3ViajFfZGZfdGVtcCwgbW90cl9hZ2dfZGYsIGJ5PWMoInRleHRfaWQiLCAid29yZF90ZXh0X2lkeCIsICJtZXRyaWMiKSkgJT4lCiAgYXJyYW5nZSh0ZXh0X2lkLCBzZW50X2lkLCB3b3JkX2lkeCkgJT4lCiAgZmlsdGVyKCEodGV4dF9pZCA9PSAxMyAmIHdvcmRfdGV4dF9pZHggPj0gMjAgJiB3b3JkX3RleHRfaWR4IDw9IDUyKSkgJT4lCiAgZmlsdGVyKCEodGV4dF9pZCA9PSAzICYgd29yZF90ZXh0X2lkeCA+PSA0NiAmIHdvcmRfdGV4dF9pZHggPD0gNTcpKSAlPiUKICByZW5hbWUod29yZCA9IHdvcmQueSkgJT4lCiAgZHBseXI6OnNlbGVjdCh0ZXh0X2lkLCB3b3JkX3RleHRfaWR4LCBtZXRyaWMsIHdvcmQsIHZhbHVlXzEpCgojIFZpZXcocHJvdm9fZXlldHJhY2tpbmdfc3ViajFfZGYpCgpwcm92b19leWV0cmFja2luZ19zdWJqMl9kZiA9IHByb3ZvX2V5ZXRyYWNraW5nX2RmICU+JQogIGZpbHRlcihzdWJqID4gNDIpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGdyb3VwX2J5KHRleHRfaWQsIHdvcmRfdGV4dF9pZHgsIHNlbnRfaWQsIHdvcmRfaWR4LCB3b3JkLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICAgIHJlbmFtZSh2YWx1ZV8yID0gdmFsdWUpJT4lCiAgZHBseXI6OnNlbGVjdCgtc2VudF9pZCwgLXdvcmRfaWR4KQoKIyBWaWV3KHByb3ZvX2V5ZXRyYWNraW5nX3N1YmoyX2RmKQogIApwcm92b19leWV0cl9ncm91cGVkX2RmID0gbWVyZ2UocHJvdm9fZXlldHJhY2tpbmdfc3ViajJfZGYsIHByb3ZvX2V5ZXRyYWNraW5nX3N1YmoxX2RmLCBieT1jKCJ0ZXh0X2lkIiwgIndvcmRfdGV4dF9pZHgiLCAibWV0cmljIikpICU+JQogICMgZmlsdGVyKHdvcmQueCA9PSB3b3JkLnkpICU+JQogIGRwbHlyOjpzZWxlY3QoLXdvcmQueSkgJT4lCiAgZ3JvdXBfYnkobWV0cmljKSAlPiUKICAgIG11dGF0ZShtb3RyX291dGxpZXIgPSBpZl9lbHNlKG1ldHJpYyAhPSAiRlBSZWciICYgbWV0cmljICE9ICJza2lwIiAmIHZhbHVlXzEgPiAobWVhbih2YWx1ZV8xKSArIDMgKiBzZCh2YWx1ZV8xKSApLCBULCBGKSkgJT4lCiAgICBmaWx0ZXIobW90cl9vdXRsaWVyID09IEYpICU+JQogICAgbXV0YXRlKGV5ZXRyX291dGxpZXIgPSBpZl9lbHNlKG1ldHJpYyAhPSAiRlBSZWciICYgbWV0cmljICE9ICJza2lwIiAmIHZhbHVlXzIgPiAobWVhbih2YWx1ZV8yKSArIDMgKiBzZCh2YWx1ZV8yKSApLCBULCBGKSkgJT4lCiAgICBmaWx0ZXIoZXlldHJfb3V0bGllciA9PSBGKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ2F0aGVyKG1lYXN1cmUsIHZhbHVlLCBjKCJ2YWx1ZV8xIiwgInZhbHVlXzIiKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtbW90cl9vdXRsaWVyLCAtZXlldHJfb3V0bGllcikKCiMgVmlldyhwcm92b19leWV0cl9ncm91cGVkX2RmKQoKYGBgCgoKYGBge3J9CnByb3ZvX2RmID0gbWVyZ2UocHJvdm9fZXlldHJhY2tpbmdfYWdnX2RmLCBwcm92b19tb2RlbGluZ19kZiwgYnk9YygidGV4dF9pZCIsICJzZW50X2lkIiwgIndvcmRfaWR4IikpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGFycmFuZ2UodGV4dF9pZCwgc2VudF9pZCwgd29yZF9pZHgpICU+JQogIHJlbmFtZShleWV0cl92YWx1ZSA9IHZhbHVlKSAKCnByb3ZvX2RmID0gbWVyZ2UocHJvdm9fZGYsIG1vdHJfYWdnX2RmLCBieT1jKCJ0ZXh0X2lkIiwgIndvcmRfdGV4dF9pZHgiLCAibWV0cmljIikpICU+JQphcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KSAlPiUKICAjIGFsbW9zdCBhbGwgdGhlIHdvcmQueCAhPSB3b3JkLnkgaXMgYmVjYXVzZSBvZiBub3JtYWxpemF0aW9uIHByb2JsZW0sIHNvIHdlIGNhbiBrZWVwIHRoZW0sIGluc3RlYWQsIGRlbGV0aW5nIHNvbWUgc3BlY2lhbCBjYXNlcwpmaWx0ZXIoISh0ZXh0X2lkID09IDEzICYgd29yZF90ZXh0X2lkeCA+PSAyMCAmIHdvcmRfdGV4dF9pZHggPD0gNTIpKSAlPiUKICBmaWx0ZXIoISh0ZXh0X2lkID09IDMgJiB3b3JkX3RleHRfaWR4ID49IDQ2ICYgd29yZF90ZXh0X2lkeCA8PSA1NykpICU+JQojIGZpbHRlcih3b3JkLnggPT0gd29yZCkgIyU+JQpkcGx5cjo6c2VsZWN0KC13b3JkLngsIC13b3JkLnkpICU+JQpncm91cF9ieShtZXRyaWMpICU+JQogIG11dGF0ZShtb3RyX291dGxpZXIgPSBpZl9lbHNlKG1ldHJpYyAhPSAiRlBSZWciICYgbW90cl92YWx1ZSA+IChtZWFuKG1vdHJfdmFsdWUpICsgMyAqIHNkKG1vdHJfdmFsdWUpICksIFQsIEYpKSAlPiUKICBmaWx0ZXIobW90cl9vdXRsaWVyID09IEYpICU+JQogIG11dGF0ZShleWV0cl9vdXRsaWVyID0gaWZfZWxzZShtZXRyaWMgIT0gIkZQUmVnIiAmIGV5ZXRyX3ZhbHVlID4gKG1lYW4oZXlldHJfdmFsdWUpICsgMyAqIHNkKGV5ZXRyX3ZhbHVlKSApLCBULCBGKSkgJT4lCiAgZmlsdGVyKGV5ZXRyX291dGxpZXIgPT0gRikgJT4lCnVuZ3JvdXAoKSAlPiUKZ2F0aGVyKG1lYXN1cmUsIHZhbHVlLCBjKCJleWV0cl92YWx1ZSIsICJtb3RyX3ZhbHVlIikpICU+JQpkcGx5cjo6c2VsZWN0KC1tb3RyX291dGxpZXIsIC1leWV0cl9vdXRsaWVyKQogIAojIFZpZXcocHJvdm9fZGYpCiMgcHJvdm9fZGYKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgbXV0YXRlKG1lYXN1cmUgPSBpZl9lbHNlKG1lYXN1cmUgPT0gImV5ZXRyX3ZhbHVlIiwgIkV5ZXRyYWNraW5nIFZhbHVlIiwgIk1vVFIgVmFsdWUiKSkgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciICYgbWV0cmljICE9ICJza2lwIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGZhY2V0X3dyYXAoLn5tZWFzdXJlLCBzY2FsZXM9ImZyZWVfeSIpICsKICAgIHhsYWIoIlJlYWRpbmcgVGltZSAobXMpIikKCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL2RlbnNpdHkucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQpgYGAKCgpgYGB7cn0KcHJpbnQoIkdhemUgRHVyYXRpb24iKQpnZF9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoZ2RfZGYkZXlldHJfdmFsdWUsIGdkX2RmJG1vdHJfdmFsdWUpJGVzdGltYXRlKQoKY29yX2RmID0gcHJvdm9fZXlldHJfZ3JvdXBlZF9kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JSBncm91cF9ieSh0ZXh0X2lkLCBtZXRyaWMsIG1lYXN1cmUpICU+JQogIHN1bW1hcml6ZSh2YWx1ZSA9IG1lYW4odmFsdWUsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcCcpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiR2YWx1ZV8xLCBjb3JfZGYkdmFsdWVfMikkZXN0aW1hdGUpCgpwcmludCgiRmlyc3QgRHVyYXRpb24iKQpmZF9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJmaXJzdF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGZkX2RmJGV5ZXRyX3ZhbHVlLCBmZF9kZiRtb3RyX3ZhbHVlKSRlc3RpbWF0ZSkKCiMgbGluZSA0ODksIDQ5MDsgMzE0NywgMzE0OCBzaGFyZSB0aGUgc2FtZSBrZXkKY29yX2RmID0gcHJvdm9fZXlldHJfZ3JvdXBlZF9kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgZ3JvdXBfYnkodGV4dF9pZCwgbWV0cmljLCBtZWFzdXJlKSAlPiUKICBzdW1tYXJpemUodmFsdWUgPSBtZWFuKHZhbHVlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQpwcmludChjb3IudGVzdChjb3JfZGYkdmFsdWVfMSwgY29yX2RmJHZhbHVlXzIpJGVzdGltYXRlKQoKcHJpbnQoIkdvIFBhc3QgVGltZSIpCgpncHRfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ29fcGFzdF90aW1lIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoZ3B0X2RmJGV5ZXRyX3ZhbHVlLCBncHRfZGYkbW90cl92YWx1ZSkkZXN0aW1hdGUpCgpjb3JfZGYgPSBwcm92b19leWV0cl9ncm91cGVkX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnb19wYXN0X3RpbWUiKSAlPiUgZ3JvdXBfYnkodGV4dF9pZCwgbWV0cmljLCBtZWFzdXJlKSAlPiUKICBzdW1tYXJpemUodmFsdWUgPSBtZWFuKHZhbHVlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQpwcmludChjb3IudGVzdChjb3JfZGYkdmFsdWVfMSwgY29yX2RmJHZhbHVlXzIpJGVzdGltYXRlKQoKcHJpbnQoIlRvdGFsIER1cmF0aW9uIikKCnRkX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gInRvdGFsX2R1cmF0aW9uIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QodGRfZGYkZXlldHJfdmFsdWUsIHRkX2RmJG1vdHJfdmFsdWUpJGVzdGltYXRlKQoKY29yX2RmID0gcHJvdm9fZXlldHJfZ3JvdXBlZF9kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAidG90YWxfZHVyYXRpb24iKSAlPiUgZ3JvdXBfYnkodGV4dF9pZCwgbWV0cmljLCBtZWFzdXJlKSAlPiUKICBzdW1tYXJpemUodmFsdWUgPSBtZWFuKHZhbHVlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQpwcmludChjb3IudGVzdChjb3JfZGYkdmFsdWVfMSwgY29yX2RmJHZhbHVlXzIpJGVzdGltYXRlKQoKcHJpbnQoIlJlZ3Jlc3Npb24iKQoKcmVnX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gIkZQUmVnIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QocmVnX2RmJGV5ZXRyX3ZhbHVlLCByZWdfZGYkbW90cl92YWx1ZSkkZXN0aW1hdGUpCgpjb3JfZGYgPSBwcm92b19leWV0cl9ncm91cGVkX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJGUFJlZyIpICU+JSBncm91cF9ieSh0ZXh0X2lkLCBtZXRyaWMsIG1lYXN1cmUpICU+JQogIHN1bW1hcml6ZSh2YWx1ZSA9IG1lYW4odmFsdWUsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcCcpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiR2YWx1ZV8xLCBjb3JfZGYkdmFsdWVfMikkZXN0aW1hdGUpCgpwcmludCgiU2tpcCIpCnNraXBfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAic2tpcCIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KHNraXBfZGYkZXlldHJfdmFsdWUsIHNraXBfZGYkbW90cl92YWx1ZSkkZXN0aW1hdGUpCgpjb3JfZGYgPSBwcm92b19leWV0cl9ncm91cGVkX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJza2lwIikgJT4lIGdyb3VwX2J5KHRleHRfaWQsIG1ldHJpYywgbWVhc3VyZSkgJT4lCiAgc3VtbWFyaXplKHZhbHVlID0gbWVhbih2YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJHZhbHVlXzEsIGNvcl9kZiR2YWx1ZV8yKSRlc3RpbWF0ZSkKCgpgYGAKCgoKYGBge3J9Cgpwcm92b19kZiAlPiUKICBmaWx0ZXIobWV0cmljICE9ICJGUFJlZyIgJiBtZXRyaWMgIT0gInNraXAiKSAlPiUKICBzcHJlYWQobWVhc3VyZSwgdmFsdWUpICU+JQogIGdncGxvdChhZXMoeCA9IG1vdHJfdmFsdWUsIHk9ZXlldHJfdmFsdWUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkgKwogICAgZ2VvbV9hYmxpbmUoc2xvcGU9MSwgaW50ZXJjZXB0PTAsIGNvbG9yID0gImJsYWNrIikgKwogICAgI3N0YXRfc3VtbWFyeV9iaW4oYmlucz0xMDAsIGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIsIHNpemUgPSAwLjA1KSArCiAgICBmYWNldF93cmFwKC5+bWV0cmljLCBzY2FsZXMgPSAiZnJlZSIsIG5yb3cgPSAxKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDUwMCksIHhsaW09YygwLCA1MDApKSArCiAgICBnZW9tX3Ntb290aCgpCgoKIyBnZ3NhdmUoIi4uL3Zpc3VhbGl6YXRpb24vbWV0cmljX2Nvci5wbmciLCBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjUpCmBgYAojIyBDb3JyZWxhdGlvbnMgdG8gV29yZC1MZXZlbCBTdGF0aXN0aWNhbCBQcm9wZXJ0aWVzCgpgYGB7cn0KcHJpbnQoIkxlbiIpCmNvcl9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJGV5ZXRyX3ZhbHVlLCBjb3JfZGYkbGVuKSRlc3RpbWF0ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJG1vdHJfdmFsdWUsIGNvcl9kZiRsZW4pJGVzdGltYXRlKQoKcHJpbnQoIkZyZXEiKQpjb3JfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQoKcHJpbnQoIlN1cnAiKQpjb3JfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJHN1cnApJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJHN1cnApJGVzdGltYXRlKQoKCmBgYAoKYGBge3J9CnByaW50KCJMZW4iKQpjb3JfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAidG90YWxfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQpwcmludChjb3IudGVzdChjb3JfZGYkZXlldHJfdmFsdWUsIGNvcl9kZiRsZW4pJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJGxlbikkZXN0aW1hdGUpCgpwcmludCgiRnJlcSIpCmNvcl9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJ0b3RhbF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQoKcHJpbnQoIlN1cnAiKQpjb3JfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAidG90YWxfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQpwcmludChjb3IudGVzdChjb3JfZGYkZXlldHJfdmFsdWUsIGNvcl9kZiRzdXJwKSRlc3RpbWF0ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJG1vdHJfdmFsdWUsIGNvcl9kZiRzdXJwKSRlc3RpbWF0ZSkKYGBgCgpgYGB7cn0KcHJpbnQoIkxlbiIpCmNvcl9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJmaXJzdF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJGxlbikkZXN0aW1hdGUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRtb3RyX3ZhbHVlLCBjb3JfZGYkbGVuKSRlc3RpbWF0ZSkKCnByaW50KCJGcmVxIikKY29yX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImZpcnN0X2R1cmF0aW9uIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJGV5ZXRyX3ZhbHVlLCBjb3JfZGYkZnJlcSkkZXN0aW1hdGUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRtb3RyX3ZhbHVlLCBjb3JfZGYkZnJlcSkkZXN0aW1hdGUpCgpwcmludCgiU3VycCIpCmNvcl9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJmaXJzdF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJHN1cnApJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJHN1cnApJGVzdGltYXRlKQpgYGAKCmBgYHtyfQpwcmludCgiTGVuIikKY29yX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdvX3Bhc3RfdGltZSIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJGxlbikkZXN0aW1hdGUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRtb3RyX3ZhbHVlLCBjb3JfZGYkbGVuKSRlc3RpbWF0ZSkKCnByaW50KCJGcmVxIikKY29yX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdvX3Bhc3RfdGltZSIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRleWV0cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQpwcmludChjb3IudGVzdChjb3JfZGYkbW90cl92YWx1ZSwgY29yX2RmJGZyZXEpJGVzdGltYXRlKQoKcHJpbnQoIlN1cnAiKQpjb3JfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ29fcGFzdF90aW1lIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKcHJpbnQoY29yLnRlc3QoY29yX2RmJGV5ZXRyX3ZhbHVlLCBjb3JfZGYkc3VycCkkZXN0aW1hdGUpCnByaW50KGNvci50ZXN0KGNvcl9kZiRtb3RyX3ZhbHVlLCBjb3JfZGYkc3VycCkkZXN0aW1hdGUpCmBgYAoKCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2F0aGVyKHdvcmRfcHJvcCwgd29yZF9wcm9wX3ZhbCwgYygiZnJlcSIsICJsZW4iLCAic3VycCIpKSAlPiUKICBtdXRhdGUobWVhc3VyZSA9IGlmX2Vsc2UobWVhc3VyZSA9PSAiZXlldHJfdmFsdWUiLCAiRXlldHJhY2tpbmcgVmFsdWUiLCAiTW9UUiBWYWx1ZSIpKSAlPiUKICBtdXRhdGUod29yZF9wcm9wID0gY2FzZV93aGVuKAogICAgd29yZF9wcm9wID09ICJmcmVxIiB+ICJGcmVxdWVuY3kiLAogICAgd29yZF9wcm9wID09ICJsZW4iIH4gIkxlbmd0aCIsCiAgICB3b3JkX3Byb3AgPT0gInN1cnAiIH4gIlN1cnByaXNhbCIKICApKSAlPiUKICBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHk9d29yZF9wcm9wX3ZhbCwgY29sb3IgPSBtZWFzdXJlKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogICAgZmFjZXRfd3JhcChtZWFzdXJlfndvcmRfcHJvcCwgc2NhbGVzPSJmcmVlIiwgc3RyaXAucG9zaXRpb24gPSAicmlnaHQiKSArCiAgICBnZW9tX3Ntb290aChjb2xvciA9ICJncmV5IikgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiCiAgKQoKIyBnZ3NhdmUoIi4uL3Zpc3VhbGl6YXRpb24vd29yZF9wcm9wX2NvbXBzLnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCgpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1mcmVxLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKYGBge3J9CnByb3ZvX2RmICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCB5PXN1cnAsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGZhY2V0X2dyaWQobWV0cmljfm1lYXN1cmUsIHNjYWxlcz0iZnJlZSIpICsKICAgIGdlb21fc21vb3RoKCkKYGBgCgoKIyMgU2hhcGUgb2Ygc3VycHJpc2FsIC8gUlQgcmVsYXRpb25zaGlwCgpgYGB7cn0KCmZpdF9nYW1faW5uZXIgPSBmdW5jdGlvbihib290c3RyYXBfc2FtcGxlLCBtZWFuX3ByZWRpY3RvcnMpIHsKICAKICBkZiA9IGJvb3RzdHJhcF9zYW1wbGUkZGF0YQogIHdlaWdodHMgPSB0YWJ1bGF0ZShhcy5pbnRlZ2VyKGJvb3RzdHJhcF9zYW1wbGUpLCBucm93KGRmKSkKICAKICBtID0gZ2FtKHBzeWNob21ldHJpYyB+IHMoc3VycCwgYnMgPSAnY3InLCBrID0gNikgKyBzKHByZXZfc3VycCwgYnMgPSAnY3InLCBrID0gNikgKyB0ZShmcmVxLCBsZW4sIGJzID0gJ2NyJykgKyB0ZShwcmV2X2ZyZXEsIHByZXZfbGVuLCBicyA9ICdjcicpLCBkYXRhID0gZGYsIHdlaWdodHMgPSB3ZWlnaHRzKQogIHRlcm1zX3RvX3ByZWRpY3QgPSBjKCJzKHN1cnApIiwgInMocHJldl9zdXJwKSIpCiAgCiAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc3VycD1zZXEoMCwyMCxieT0wLjEpLCBwcmV2X3N1cnA9bWVhbl9wcmVkaWN0b3JzJHN1cnAsCiAgICAgICAgICAgICAgICAgICAgICAgI3N1cnA9bWVhbl9wcmVkaWN0b3JzJHN1cnAsIHByZXZfc3VycD1zZXEoMCwyMCxieT0wLjEpLAogICAgICAgICAgICAgICAgICAgICAgIGZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsIHByZXZfZnJlcT1tZWFuX3ByZWRpY3RvcnMkZnJlcSwKICAgICAgICAgICAgICAgICAgICAgICBsZW49bWVhbl9wcmVkaWN0b3JzJGxlbiwgcHJldl9sZW49bWVhbl9wcmVkaWN0b3JzJGxlbikKICAgICAgICAgICAgICAgICAgICAgICAjIGxlbj1tZWFuX3ByZWRpY3RvcnMkZnJlcSwgcHJldl9sZW49bWVhbl9wcmVkaWN0b3JzJGZyZXEpICMjPz8/PwoKICAjIFJldHVybnMgYSBtYXRyaXggTl9zYW1wbGVzICogTl90ZXJtcy4KICBwZXJfdGVybV9wcmVkaWN0aW9ucyA9IHByZWRpY3QobSwgbmV3ZGF0YT1uZXdkYXRhLCB0ZXJtcz10ZXJtc190b19wcmVkaWN0LCB0eXBlPSJ0ZXJtcyIpCgogICMgQWRkaXRpdmUgbW9kZWwgLS0gc3VtIGFjcm9zcyBwcmVkaWN0b3IgcmVzcG9uc2UgY29udHJpYnV0aW9ucyAobWF0cml4IGNvbHVtbnMpLgogIHByZWRpY3Rpb25zID0gcm93U3VtcyhwZXJfdGVybV9wcmVkaWN0aW9ucykKICAKICAjIHByaW50KCJGaXR0ZWQgR0FNIG1vZGVsOiIpCiAgIyBwcmludChtKQoKICAjIHByaW50KCJQcmVkaWN0ZWQgdmFsdWVzIGZvciBzcGVjaWZpZWQgc21vb3RoIHRlcm1zOiIpCiAgIyBwcmludChwZXJfdGVybV9wcmVkaWN0aW9ucykKCiAgcmV0dXJuKG5ld2RhdGEgJT4lIG11dGF0ZSh5PXByZWRpY3Rpb25zKSkKfQoKZml0X2dhbSA9IGZ1bmN0aW9uKGRmLCBtZWFuX3ByZWRpY3RvcnMsIGFscGhhPTAuMDUpIHsKICAjIEJvb3RzdHJhcC1yZXNhbXBsZSBkYXRhCiAgYm9vdF9tb2RlbHMgPSBkZiAlPiUgYm9vdHN0cmFwcyh0aW1lcz0xMCkgJT4lIAogICAjIEZpdCBhIEdBTSBhbmQgZ2V0IHByZWRpY3Rpb25zIGZvciBlYWNoIHNhbXBsZQogICAgbXV0YXRlKHNtb290aGVkPW1hcChzcGxpdHMsIGZpdF9nYW1faW5uZXIsIG1lYW5fcHJlZGljdG9ycz1tZWFuX3ByZWRpY3RvcnMpKQogIAogICMgRXh0cmFjdCBtZWFuIGFuZCA1JSBhbmQgOTUlIHBlcmNlbnRpbGUgeS12YWx1ZXMgZm9yIGVhY2ggc3VycHJpc2FsIHZhbHVlCiAgcmVzdWx0ID0gYm9vdF9tb2RlbHMgJT4lIAogICAgdW5uZXN0KHNtb290aGVkKSAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KHN1cnAsIHkpICU+JQogICAgIyBkcGx5cjo6c2VsZWN0KHByZXZfc3VycCwgeSkgJT4lCiAgICBncm91cF9ieShzdXJwKSAlPiUKICAgICMgZ3JvdXBfYnkocHJldl9zdXJwKSAlPiUKICAgICAgc3VtbWFyaXNlKHlfbG93ZXI9cXVhbnRpbGUoeSwgYWxwaGEgLyAyKSwgCiAgICAgICAgICAgICAgICB5X3VwcGVyPXF1YW50aWxlKHksIDEgLSBhbHBoYSAvIDIpLAogICAgICAgICAgICAgICAgeT1tZWFuKHkpKSAlPiUgCiAgICB1bmdyb3VwKCkKICAjIHByaW50KHJlc3VsdCkKICAKICByZXR1cm4gKHJlc3VsdCkKfQoKYGBgCgoKYGBge3J9CgpnYW1fbW9kZWxpbmdfZGYgPSBwcm92b19kZiAlPiUKICBzcHJlYWQobWVhc3VyZSwgdmFsdWUpICU+JQogICMgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUgICMgbGVuIGhhcyBhbHJlYWR5IGV4aXN0cywgYnV0IGRvIG5vdCBjb3VudCBwdW5jdCBpbnRvIGxlbi4KICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCiMgVmlldyhnYW1fbW9kZWxpbmdfZGYpCgpzbW9vdGhzX2RmID0gZGF0YS5mcmFtZSgpCgptZXRyaWNzID0gYygiZ2F6ZV9kdXJhdGlvbiIsICJ0b3RhbF9kdXJhdGlvbiIsICJnb19wYXN0X3RpbWUiLCAiZmlyc3RfZHVyYXRpb24iKQojIG1ldHJpY3MgPSBjKCJnYXplX2R1cmF0aW9uIikKZm9yIChtIGluIG1ldHJpY3MpIHsKICBwcmludChwYXN0ZTAoIkZpdHRpbmcgbW9kZWwgZm9yICIsIG0pKQogIGR1bW15X2RmID0gZ2FtX21vZGVsaW5nX2RmICU+JSBmaWx0ZXIobWV0cmljID09IG0pCiAgbWVhbl9wcmVkaWN0b3JzID0gZHVtbXlfZGYgJT4lIHN1bW1hcmlzZShzdXJwID0gbWVhbihzdXJwKSwgbGVuID0gbWVhbihsZW4pLCBmcmVxID0gbWVhbihmcmVxKSkKICBzbW9vdGhzID0gZHVtbXlfZGYgJT4lIGZpdF9nYW0oLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCiAgIyBWaWV3KHNtb290aHNfZGYpCn0KCmBgYAoKIyMjIEdldCBEZW5zaXR5IERhdGEKCmBgYHtyfQpnZXRfZF9wb2ludHMgPSBmdW5jdGlvbihkZikgewogICAgeCA9IGRlbnNpdHkoZGYkc3VycCkkeAogICAgeSA9IGRlbnNpdHkoZGYkc3VycCkkeQogICAgcmV0dXJuKGRhdGEuZnJhbWUoeCwgeSkpCiAgfQoKZGVuc2l0eV9kYXRhID0gZGF0YS5mcmFtZSgpCgpmb3IobSBpbiBjKCJnYXplX2R1cmF0aW9uIiwgInRvdGFsX2R1cmF0aW9uIiwgImdvX3Bhc3RfdGltZSIsICJmaXJzdF9kdXJhdGlvbiIpKSB7CiAgZHVtbXlfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSBtKSAlPiUKICAgICAgZG8oe2dldF9kX3BvaW50cyguKX0pICU+JQogICAgICBmaWx0ZXIoeD4wLCB4PDIwKQogIGRlbnNpdHlfZGF0YSA9IHJiaW5kKGRlbnNpdHlfZGF0YSwgZHVtbXlfZGYgJT4lIG11dGF0ZShtZXRyaWM9bSkpCn0KCmBgYAoKCmBgYHtyfQoKIyBTdXJwcmlzYWwgY3VydmVzCiAgZ2dwbG90KCkgKwogICAgICAjIERlbnNpdHkgRGF0YQogICAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MCwgeG1heD0yMCwgeW1pbj0tMjAseW1heD0tMTAsIGZpbGw9IiNmNGY0ZjQiLCBjb2xvcj0iZ3JleSIsIGFscGhhPTEsIHNpemUgPSAwKSArCiAgICAgIGdlb21fbGluZShkYXRhID0gZGVuc2l0eV9kYXRhLCBhZXMoeD14LCB5PXkqNTAgLSAxOCksIGNvbG9yPSIjYWFhYWFhIiwgc2l6ZSA9IDAuNCkgKwogICAgICAjIFN1cnJwIC8gUnQgZGF0YQogICAgICAjIGdlb21fbGluZShkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9cHJldl9zdXJwLCB5PXksIGNvbG9yID0gcHN5Y2hvbWV0cmljKSwgc2l6ZT0wLjcpICsKICAgICAgZ2VvbV9saW5lKGRhdGEgPSBzbW9vdGhzX2RmLCBhZXMoeD1zdXJwLCB5PXksIGNvbG9yID0gcHN5Y2hvbWV0cmljKSwgc2l6ZT0wLjcpICsKICAgICAgIyBnZW9tX3JpYmJvbihkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9cHJldl9zdXJwLCB5bWluPXlfbG93ZXIsIHltYXg9eV91cHBlciwgZmlsbCA9IHBzeWNob21ldHJpYyksIGFscGhhPTAuMywgc2l6ZT0wLjUpICsKICAgICAgZ2VvbV9yaWJib24oZGF0YSA9IHNtb290aHNfZGYsIGFlcyh4PXN1cnAsIHltaW49eV9sb3dlciwgeW1heD15X3VwcGVyLCBmaWxsID0gcHN5Y2hvbWV0cmljKSwgYWxwaGE9MC4zLCBzaXplPTAuNSkgKwogICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPWMoMCwgMTAsIDIwKSwgYnJlYWtzPWMoMCwgMTAsIDIwKSwgbWlub3JfYnJlYWtzID0gTlVMTCkgKwogICAgICBmYWNldF93cmFwKHBzeWNob21ldHJpY34uLCBucm93ID0gMSkgKwogICAgICB5bGFiKCJTbG93ZG93biBkdWUgdG8gU3VycHJpc2FsIChtcykiKSArCiAgICAgIHhsYWIoIlN1cnByaXNhbCBvZiBXb3JkIikgKwogICAgICAjIGdndGl0bGUoIk1vVFIgVGltZXMgYW5kIFByZXZpb3VzIFdvcmQgU3VycHJpc2FsIikKICAgICAgZ2d0aXRsZSgiTW9UUiBUaW1lcyBhbmQgQ3VycmVudCBXb3JkIFN1cnByaXNhbCIpCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgKQogIAojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9zdXJwcmlzYWxfcnRfbGluay5wbmciLCBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjUpCgoKYGBgCgoKYGBge3J9CmZpdF9nYW1faW5uZXJfMiA9IGZ1bmN0aW9uKGJvb3RzdHJhcF9zYW1wbGUsIG1lYW5fcHJlZGljdG9ycykgewogIAogIGRmID0gYm9vdHN0cmFwX3NhbXBsZSRkYXRhCiAgd2VpZ2h0cyA9IHRhYnVsYXRlKGFzLmludGVnZXIoYm9vdHN0cmFwX3NhbXBsZSksIG5yb3coZGYpKQogIAogIG0gPSBnYW0ocHN5Y2hvbWV0cmljIH4gcyhzdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHMocHJldl9zdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHRlKGZyZXEsIGxlbiwgYnMgPSAnY3InKSArIHRlKHByZXZfZnJlcSwgcHJldl9sZW4sIGJzID0gJ2NyJyksIGRhdGEgPSBkZiwgd2VpZ2h0cyA9IHdlaWdodHMpCiAgdGVybXNfdG9fcHJlZGljdCA9IGMoInMoc3VycCkiLCAicyhwcmV2X3N1cnApIikKICAKICBuZXdkYXRhID0gZGF0YS5mcmFtZShzdXJwPW1lYW5fcHJlZGljdG9ycyRzdXJwLCBwcmV2X3N1cnA9c2VxKDAsMjAsYnk9MC4xKSwKICAgICAgICAgICAgICAgICAgICAgICBmcmVxPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2ZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgbGVuPW1lYW5fcHJlZGljdG9ycyRsZW4sIHByZXZfbGVuPW1lYW5fcHJlZGljdG9ycyRsZW4pCgogICMgUmV0dXJucyBhIG1hdHJpeCBOX3NhbXBsZXMgKiBOX3Rlcm1zLgogIHBlcl90ZXJtX3ByZWRpY3Rpb25zID0gcHJlZGljdChtLCBuZXdkYXRhPW5ld2RhdGEsIHRlcm1zPXRlcm1zX3RvX3ByZWRpY3QsIHR5cGU9InRlcm1zIikKCiAgIyBBZGRpdGl2ZSBtb2RlbCAtLSBzdW0gYWNyb3NzIHByZWRpY3RvciByZXNwb25zZSBjb250cmlidXRpb25zIChtYXRyaXggY29sdW1ucykuCiAgcHJlZGljdGlvbnMgPSByb3dTdW1zKHBlcl90ZXJtX3ByZWRpY3Rpb25zKQoKICByZXR1cm4obmV3ZGF0YSAlPiUgbXV0YXRlKHk9cHJlZGljdGlvbnMpKQp9CgpmaXRfZ2FtXzIgPSBmdW5jdGlvbihkZiwgbWVhbl9wcmVkaWN0b3JzLCBhbHBoYT0wLjA1KSB7CiAgIyBCb290c3RyYXAtcmVzYW1wbGUgZGF0YQogIGJvb3RfbW9kZWxzID0gZGYgJT4lIGJvb3RzdHJhcHModGltZXM9MTApICU+JSAKICAgIyBGaXQgYSBHQU0gYW5kIGdldCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzYW1wbGUKICAgIG11dGF0ZShzbW9vdGhlZD1tYXAoc3BsaXRzLCBmaXRfZ2FtX2lubmVyXzIsIG1lYW5fcHJlZGljdG9ycz1tZWFuX3ByZWRpY3RvcnMpKQogIAogICMgRXh0cmFjdCBtZWFuIGFuZCA1JSBhbmQgOTUlIHBlcmNlbnRpbGUgeS12YWx1ZXMgZm9yIGVhY2ggc3VycHJpc2FsIHZhbHVlCiAgcmVzdWx0ID0gYm9vdF9tb2RlbHMgJT4lIAogICAgdW5uZXN0KHNtb290aGVkKSAlPiUgCiAgICAjIGRwbHlyOjpzZWxlY3Qoc3VycCwgeSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KHByZXZfc3VycCwgeSkgJT4lCiAgICAjIGdyb3VwX2J5KHN1cnApICU+JQogICAgZ3JvdXBfYnkocHJldl9zdXJwKSAlPiUKICAgICAgc3VtbWFyaXNlKHlfbG93ZXI9cXVhbnRpbGUoeSwgYWxwaGEgLyAyKSwgCiAgICAgICAgICAgICAgICB5X3VwcGVyPXF1YW50aWxlKHksIDEgLSBhbHBoYSAvIDIpLAogICAgICAgICAgICAgICAgeT1tZWFuKHkpKSAlPiUgCiAgICB1bmdyb3VwKCkKICAjIHByaW50KHJlc3VsdCkKICAKICByZXR1cm4gKHJlc3VsdCkKfQpgYGAKCgpgYGB7cn0KZ2FtX21vZGVsaW5nX2RmXzIgPSBwcm92b19kZiAlPiUKICBzcHJlYWQobWVhc3VyZSwgdmFsdWUpICU+JQogICMgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUgICMgbGVuIGhhcyBhbHJlYWR5IGV4aXN0cywgYnV0IGRvIG5vdCBjb3VudCBwdW5jdCBpbnRvIGxlbi4KICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCgpzbW9vdGhzX2RmID0gZGF0YS5mcmFtZSgpCgptZXRyaWNzID0gYygiZ2F6ZV9kdXJhdGlvbiIsICJ0b3RhbF9kdXJhdGlvbiIsICJnb19wYXN0X3RpbWUiLCAiZmlyc3RfZHVyYXRpb24iKQpmb3IgKG0gaW4gbWV0cmljcykgewogIHByaW50KHBhc3RlMCgiRml0dGluZyBtb2RlbCBmb3IgIiwgbSkpCiAgZHVtbXlfZGYgPSBnYW1fbW9kZWxpbmdfZGZfMiAlPiUgZmlsdGVyKG1ldHJpYyA9PSBtKQogIG1lYW5fcHJlZGljdG9ycyA9IGR1bW15X2RmICU+JSBzdW1tYXJpc2Uoc3VycCA9IG1lYW4oc3VycCksIGxlbiA9IG1lYW4obGVuKSwgZnJlcSA9IG1lYW4oZnJlcSkpCiAgc21vb3RocyA9IGR1bW15X2RmICU+JSBmaXRfZ2FtXzIoLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCn0KYGBgCgpgYGB7cn0KZ2V0X2RfcG9pbnRzID0gZnVuY3Rpb24oZGYpIHsKICAgIHggPSBkZW5zaXR5KGRmJHN1cnApJHgKICAgIHkgPSBkZW5zaXR5KGRmJHN1cnApJHkKICAgIHJldHVybihkYXRhLmZyYW1lKHgsIHkpKQogIH0KCmRlbnNpdHlfZGF0YSA9IGRhdGEuZnJhbWUoKQoKZm9yKG0gaW4gYygiZ2F6ZV9kdXJhdGlvbiIsICJ0b3RhbF9kdXJhdGlvbiIsICJnb19wYXN0X3RpbWUiLCAiZmlyc3RfZHVyYXRpb24iKSkgewogIGR1bW15X2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gbSkgJT4lCiAgICAgIGRvKHtnZXRfZF9wb2ludHMoLil9KSAlPiUKICAgICAgZmlsdGVyKHg+MCwgeDwyMCkKICBkZW5zaXR5X2RhdGEgPSByYmluZChkZW5zaXR5X2RhdGEsIGR1bW15X2RmICU+JSBtdXRhdGUobWV0cmljPW0pKQp9CmBgYAoKYGBge3J9CiMgU3VycHJpc2FsIGN1cnZlcwogIGdncGxvdCgpICsKICAgICAgIyBEZW5zaXR5IERhdGEKICAgICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPTAsIHhtYXg9MjAsIHltaW49LTIwLHltYXg9LTEwLCBmaWxsPSIjZjRmNGY0IiwgY29sb3I9ImdyZXkiLCBhbHBoYT0xLCBzaXplID0gMCkgKwogICAgICBnZW9tX2xpbmUoZGF0YSA9IGRlbnNpdHlfZGF0YSwgYWVzKHg9eCwgeT15KjUwIC0gMTgpLCBjb2xvcj0iI2FhYWFhYSIsIHNpemUgPSAwLjQpICsKICAgICAgIyBTdXJycCAvIFJ0IGRhdGEKICAgICAgZ2VvbV9saW5lKGRhdGEgPSBzbW9vdGhzX2RmLCBhZXMoeD1wcmV2X3N1cnAsIHk9eSwgY29sb3IgPSBwc3ljaG9tZXRyaWMpLCBzaXplPTAuNykgKwogICAgICBnZW9tX3JpYmJvbihkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9cHJldl9zdXJwLCB5bWluPXlfbG93ZXIsIHltYXg9eV91cHBlciwgZmlsbCA9IHBzeWNob21ldHJpYyksIGFscGhhPTAuMywgc2l6ZT0wLjUpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1jKDAsIDEwLCAyMCksIGJyZWFrcz1jKDAsIDEwLCAyMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsKICAgICAgZmFjZXRfd3JhcChwc3ljaG9tZXRyaWN+LiwgbnJvdyA9IDEpICsKICAgICAgeWxhYigiU2xvd2Rvd24gZHVlIHRvIFN1cnByaXNhbCAobXMpIikgKwogICAgICB4bGFiKCJTdXJwcmlzYWwgb2YgV29yZCIpICsKICAgICAgZ2d0aXRsZSgiTW9UUiBUaW1lcyBhbmQgUHJldmlvdXMgV29yZCBTdXJwcmlzYWwiKQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgojIyBwcmVjaXNpb24gYW5kIHJlY2FsbCBmb3IgRlBSZWcKYGBge3J9CkZQUmVnX2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gIkZQUmVnIikgJT4lIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkKIyB3cml0ZS5jc3YoRlBSZWdfZGYsIGZpbGUgPSAiL1VzZXJzL2N1aS9EZXNrdG9wL01vVFIvcGlwZWxpbmUvYW5jaWxsYXJ5X2RhdGEvRlBSZWcuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmNvbmZ1c2lvbl9tYXRyaXggPC0gdGFibGUoRlBSZWdfZGYkbW90cl92YWx1ZSA+IDAsIEZQUmVnX2RmJGV5ZXRyX3ZhbHVlID4gMCkKY29uZnVzaW9uX21hdHJpeAoKdHJ1ZV9wb3NpdGl2ZXMgPC0gY29uZnVzaW9uX21hdHJpeFsyLCAyXQpmYWxzZV9wb3NpdGl2ZXMgPC0gY29uZnVzaW9uX21hdHJpeFsyLCAxXQpmYWxzZV9uZWdhdGl2ZXMgPC0gY29uZnVzaW9uX21hdHJpeFsxLCAyXQoKcHJlY2lzaW9uIDwtIHRydWVfcG9zaXRpdmVzIC8gKHRydWVfcG9zaXRpdmVzICsgZmFsc2VfcG9zaXRpdmVzKQpyZWNhbGwgPC0gdHJ1ZV9wb3NpdGl2ZXMgLyAodHJ1ZV9wb3NpdGl2ZXMgKyBmYWxzZV9uZWdhdGl2ZXMpCgpwcmludCgicHJlY2lzaW9uIG9mIE1vdHIgRlBSZWc6IikKcHJpbnQocHJlY2lzaW9uKQpwcmludCgiUmVjYWxsIG9mIE1vdHIgRlBSZWc6IikKcHJpbnQocmVjYWxsKQpgYGAKCiMjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZpcnN0IGR1cmF0aW9uIGFuZCByZWdyZXNzaW9uIHByb2JhYmlsaXRpZXMuICAtLT4gZXhwbG9yYXRvcnksIG5vdCBmaW5pc2hlZC4KCmBgYHtyfQojIE1vdHIgLS0+IGZpdCBhIGxpbmVhciBtb2RlbCBmb3IgZmlyc3RfZHVyYXRpb24uIGZpbmRpbmc6IE1vcmUgRlBSZWcsIG1vcmUgZmlyc3QgZHVyYXRpb24uIAoKcmx0X2RmID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gIkZQUmVnIiB8IG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgCiAgZmlsdGVyKHN1cnAgPiAxMCkgJT4lCiAgc3ByZWFkKG1ldHJpYywgdmFsdWUpICU+JQogIGRyb3BfbmEoKQoKIyBWaWV3KHJsdF9kZikKCm1vdHJfcmx0ID0gcmx0X2RmICU+JSBmaWx0ZXIobWVhc3VyZSA9PSAibW90cl92YWx1ZSIpICU+JQogIG11dGF0ZShGUFJlZ19iaSA9IGlmX2Vsc2UoRlBSZWcgPT0gMCwgMCwgMSksCiAgICAgICAgIGZkX25vcm1hbGl6ZWQgPSBmaXJzdF9kdXJhdGlvbiAvIGxlbikKCm1vdHJfcmx0JEZQUmVnX2JpIDwtIGZhY3Rvcihtb3RyX3JsdCRGUFJlZ19iaSkKCiMgVmlldyhtb3RyX3JsdCkKCiMgbGV2ZWxzKG1vdHJfcmx0JEZQUmVnX2JpKQoKIyBtb3RyX2xtID0gbG0oZmlyc3RfZHVyYXRpb24gfiBmcmVxICsgRlBSZWdfYmksIGRhdGEgPSBtb3RyX3JsdCkKbW90cl9sbSA9IGxtKGZkX25vcm1hbGl6ZWQgfiBGUFJlZywgZGF0YSA9IG1vdHJfcmx0KQoKc3VtbWFyeShtb3RyX2xtKQoKIyB3cml0ZS5jc3Yocmx0X2RmLCBmaWxlID0gIi9Vc2Vycy9jdWkvRGVza3RvcC9Nb1RSL3BpcGVsaW5lL2FuY2lsbGFyeV9kYXRhL3JsdC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCmBgYHtyfQojIGV5cnRyIC0+IHNhbWUgYXMgbW90ciwgbW9yZSBGUFJlZywgbW9yZSBmaXJzdF9kdXJhdGlvbgoKZXlldHJfcmx0ID0gcmx0X2RmICU+JSBmaWx0ZXIobWVhc3VyZSA9PSAiZXlldHJfdmFsdWUiKSAlPiUKICBtdXRhdGUoRlBSZWdfYmkgPSBpZl9lbHNlKEZQUmVnID09IDAsIDAsIDEpLAogICAgICAgICBmZF9ub3JtYWxpemVkID0gZmlyc3RfZHVyYXRpb24gLyBsZW4pCgpleWV0cl9ybHQkRlBSZWdfYmkgPC0gZmFjdG9yKGV5ZXRyX3JsdCRGUFJlZ19iaSkKCiMgZXlldHJfbG0gPSBsbShmaXJzdF9kdXJhdGlvbiB+IEZQUmVnX2JpLCBkYXRhID0gZXlldHJfcmx0KQpleWV0cl9sbSA9IGxtKGZkX25vcm1hbGl6ZWQgfiBGUFJlZywgZGF0YSA9IGV5ZXRyX3JsdCkKCnN1bW1hcnkoZXlldHJfbG0pCgpgYGAKCgoK